﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RPGServer.Contracts.Requests
{
    class RequestFA
    {
        public static void Process(RPGPlayer player, byte[] buffer, int length)
        {
            int offset = 2;
            uint fightId = IntUtil.GetUInt(buffer, ref offset);
            uint playerId = IntUtil.GetUInt(buffer, ref offset);

            uint object01 = IntUtil.GetUInt(buffer, ref offset);
            byte action01 = IntUtil.getByte(buffer, ref offset);
            uint targetPlayer01 = IntUtil.GetUInt(buffer, ref offset);
            uint targetObject01 = IntUtil.GetUInt(buffer, ref offset);

            uint object02 = IntUtil.GetUInt(buffer, ref offset);
            byte action02 = IntUtil.getByte(buffer, ref offset);
            uint targetPlayer02 = IntUtil.GetUInt(buffer, ref offset);
            uint targetObject02 = IntUtil.GetUInt(buffer, ref offset);

            List<RPGObject> objList = new List<RPGObject>();

            RPGFight fight = CacheData.GetInstance().GetFight(0, fightId);
            if (fight != null)
            {
                // 计算战斗结果
                for (int index = 0; index < fight.Force1.Length; index++)
                {
                    RPGObject obj = fight.Force1[index];
                    if (obj != null && obj.HP > 0)
                    {
                        AddObjectToList(objList, obj);
                    }
                }

                for (int index = 0; index < fight.Force2.Length; index++)
                {
                    RPGObject obj = fight.Force2[index];
                    if (obj != null && obj.HP > 0)
                    {
                        AddObjectToList(objList, obj);
                    }
                }
            }
            else
            {
                // 出错了过期的战斗信息
                NotifyErrorToPlayer(player);
                return;
            }

            // 判断是否可以进行攻击计算了，判断是否可以执行攻击计算是根据玩家是否都发出了攻击指令来判断
            bool canRun = true;
            for (int index = 0; index < objList.Count; index++)
            {
                RPGObject obj = objList[index];
                
                if (obj.ObjectId == object01 && obj.PlayerId == playerId)
                {
                    obj.Action = action01;
                    obj.TargetPlayerId = targetPlayer01;
                    obj.TargetObjectId = targetObject01;
                }
                else if (obj.ObjectId == object02 && obj.PlayerId == playerId)
                {
                    obj.Action = action02;
                    obj.TargetPlayerId = targetPlayer02;
                    obj.TargetObjectId = targetObject02;
                }

                if (obj.IsPlayer && obj.TargetPlayerId == 0 && obj.TargetObjectId == 0 && obj.Action == 0)
                {
                    canRun = false;
                }
            }

            List<RPGFightResult> results = new List<RPGFightResult>();

            // 按照敏捷度的高低来执行攻击
            if (canRun)
            {
                // 被攻击的单位
                RPGObject curTargetObj = null;
                // 对被攻击的单位照成的伤害
                ushort curDamage = 0;

                RPGFightResult curResult = null;
                for (int index = 0; index < objList.Count; index++)
                {
                    RPGObject obj = objList[index];
                    if (obj.HP <= 0)
                    {
                        // 死亡了的单位不再战斗
                        continue;
                    }

                    if (obj.IsPlayer)
                    {
                        // 如果是玩家的单位判断是否是攻击的同一个目标，连续的攻击单位且攻击了同一个目标则形成连击
                        if (curTargetObj == null)
                        {
                            // 第一个开始攻击的单位 
                            curTargetObj = GetObject(fight, obj.TargetPlayerId, obj.TargetObjectId);
                            if (curTargetObj.HP > 0)
                            {
                                curDamage = 15;

                                curResult = new RPGFightResult();

                                curResult.AttackObjectIdList.Add(obj.ObjectId);
                                curResult.AttackPlayerIdList.Add(obj.PlayerId);
                                curResult.AttackObjectDamage = 0;

                                curResult.UnderAttackObjectId = obj.TargetObjectId;
                                curResult.UnderAttackPlayerId = obj.TargetPlayerId;
                                curResult.UnderAttackObjectDamage = curDamage;

                                DecreaseHP(curTargetObj, curDamage);

                                results.Add(curResult);
                            }
                        }
                        else if (obj.TargetPlayerId == curTargetObj.PlayerId && obj.TargetObjectId == curTargetObj.ObjectId && curDamage != 0)
                        {
                            ushort damage = 1;

                            curDamage += damage;

                            // 和上一个攻击的是同一个目标
                            curResult.AttackObjectIdList.Add(obj.ObjectId);
                            curResult.AttackPlayerIdList.Add(obj.PlayerId);
                            curResult.AttackObjectDamage = 0;

                            DecreaseHP(curTargetObj, damage);

                            curResult.UnderAttackObjectDamage = curDamage;
                        }
                        else
                        {
                            // 开始攻击了一个新的目标
                            curTargetObj = GetObject(fight, obj.TargetPlayerId, obj.TargetObjectId);
                            if (curTargetObj.HP > 0)
                            {
                                curDamage = 1;

                                curResult = new RPGFightResult();
                                curResult.AttackObjectIdList.Add(obj.ObjectId);
                                curResult.AttackPlayerIdList.Add(obj.PlayerId);
                                curResult.AttackObjectDamage = 0;

                                curResult.UnderAttackObjectId = curTargetObj.ObjectId;
                                curResult.UnderAttackPlayerId = curTargetObj.PlayerId;
                                curResult.UnderAttackObjectDamage = curDamage;

                                DecreaseHP(curTargetObj, curDamage);

                                results.Add(curResult);
                            }
                        }
                    }
                    else
                    {
                        // 电脑玩家随机找一个敌人来攻击
                        RPGObject randomTarget = GetRandomEnemyObject(fight, obj.PlayerId, obj.ObjectId);
                        if(randomTarget != null)
                        {
                            curDamage = 1;
                            curResult = new RPGFightResult();
                        
                            curResult.AttackObjectIdList.Add(obj.ObjectId);  
                            curResult.AttackPlayerIdList.Add(obj.PlayerId);
                            curResult.AttackObjectDamage = 0;

                            curResult.UnderAttackObjectId = randomTarget.ObjectId;
                            curResult.UnderAttackPlayerId = randomTarget.PlayerId;
                            curResult.UnderAttackObjectDamage += curDamage;
                            // 减少目标的hp
                            DecreaseHP(randomTarget, curDamage);

                            results.Add(curResult);
                        }
                    }
                }
            }

            // 检查战斗是否结束
            #region 检查
            bool isFinished = true;
            for (int index = 0; index < fight.Force1.Length; index++)
            {
                if (fight.Force1[index] != null && fight.Force1[index].HP > 0)
                {
                    isFinished = false;
                    break;
                }
            }
            if (!isFinished)
            {
                isFinished = true;
                for (int index = 0; index < fight.Force2.Length; index++)
                {
                    if (fight.Force2[index] != null && fight.Force2[index].HP > 0)
                    {
                        isFinished = false;
                        break;
                    }
                }
            }
            #endregion

            if (isFinished)
            {
                fight.IsFinished = true;
            }

            if (results.Count > 0)
            {
                // 通知所有玩家战斗信息
                NotifyToPlayer(fight, results);
                
            }

            if (fight.IsFinished)
            {
                Log.Debug("rpg-fa", "战斗{0}结束，删除", fight.Id);
                // 删除这场战斗
                CacheData.GetInstance().RemoveFight(fight);
            }
        }

        private static void AddObjectToList(List<RPGObject> objList, RPGObject obj)
        {
            for (int index = 0; index < objList.Count; index++)
            {
                if (objList[index].AGI < obj.AGI)
                {
                    objList.Insert(index, obj);
                    return;
                }
            }

            objList.Add(obj);
        }

        private static void NotifyToPlayer(RPGFight fight, List<RPGFightResult> results)
        {
            byte[] data = new byte[512];
            int offset = 0;
            // scenario
            IntUtil.ByteToBuffer(RPGNET_SCENARIO.GAME, data, offset, out offset);
            // cmd
            IntUtil.ByteToBuffer(RPGNET_RESPONSE.FA, data, offset, out offset);
            // error code
            IntUtil.ByteToBuffer(RPGNET_RESPONSE_ERROR.NOERROR, data, offset, out offset);
            // fight id
            IntUtil.SetUInt(fight.Id, data, ref offset);
            // round count
            IntUtil.SetByte((byte)results.Count, data, ref offset);
            for (int roundIndex = 0; roundIndex < results.Count; roundIndex++)
            {
                RPGFightResult result = results[roundIndex];
                // round id
                IntUtil.SetByte((byte)roundIndex, data, ref offset);
                // attack object count
                IntUtil.SetByte((byte)result.AttackObjectIdList.Count, data, ref offset);
                for (int atkObjIndex = 0; atkObjIndex < result.AttackObjectIdList.Count; atkObjIndex++)
                {
                    IntUtil.SetUInt((uint)result.AttackObjectIdList[atkObjIndex], data, ref offset);
                    IntUtil.SetUInt((uint)result.AttackPlayerIdList[atkObjIndex], data, ref offset);
                    IntUtil.SetUShort(result.AttackObjectDamage, data, ref offset);
                }

                IntUtil.SetUInt((uint)result.UnderAttackObjectId, data, ref offset);
                IntUtil.SetUInt((uint)result.UnderAttackPlayerId, data, ref offset);
                IntUtil.SetUShort(result.UnderAttackObjectDamage, data, ref offset);
            }
            //fight stauts
            if (fight.IsFinished)
            {
                IntUtil.SetByte(1, data, ref offset);
            }
            else
            {
                IntUtil.SetByte(2, data, ref offset);
            }


            for (int index = 0; index < fight.Force1.Length; index++)
            {
                RPGObject obj = fight.Force1[index];
                if (obj != null && obj.IsPlayer)
                {
                    obj.Player.Client.SendData(data, offset);
                }
            }

            for (int index = 0; index < fight.Force2.Length; index++)
            {
                RPGObject obj = fight.Force2[index];
                if (obj != null && obj.IsPlayer)
                {
                }
            }
        }

        private static void NotifyErrorToPlayer(RPGPlayer player)
        {
            byte[] data = new byte[512];
            int offset = 0;
            // scenario
            IntUtil.ByteToBuffer(RPGNET_SCENARIO.GAME, data, offset, out offset);
            // cmd
            IntUtil.ByteToBuffer(RPGNET_RESPONSE.FA, data, offset, out offset);
            // error code
            IntUtil.ByteToBuffer(RPGNET_RESPONSE_ERROR.UNKNOWN, data, offset, out offset);

            player.Client.SendData(data, offset);
        }

        private static void DescHP(RPGFight fight, uint playerId, uint objectId, int damage)
        {
            for (int index = 0; index < fight.Force1.Length; index++)
            {
                RPGObject obj = fight.Force1[index];
                if (obj != null && obj.PlayerId == playerId && obj.ObjectId == objectId)
                {
                    obj.HP -= (ushort)damage;
                    if (obj.HP < 0)
                    {
                        obj.HP = 0;
                        return;
                    }
                }
            }

            for (int index = 0; index < fight.Force2.Length; index++)
            {
                RPGObject obj = fight.Force2[index];
                if (obj != null && obj.PlayerId == playerId && obj.ObjectId == objectId)
                {
                    obj.HP -= (ushort)damage;
                    if (obj.HP < 0)
                    {
                        obj.HP = 0;
                        return;
                    }
                }
            }
        }

        private static void DecreaseHP(RPGObject obj, ushort damage)
        {
            if (obj.HP < damage)
            {
                obj.HP = 0;
            }
            else
            {
                obj.HP -= damage;
            }
        }

        private static RPGObject GetObject(RPGFight fight, uint playerId, uint objectId)
        {
            for (int index = 0; index < fight.Force1.Length; index++)
            {
                RPGObject obj = fight.Force1[index];
                if (obj != null && obj.PlayerId == playerId && obj.ObjectId == objectId)
                {
                    return obj;
                }
            }

            for (int index = 0; index < fight.Force2.Length; index++)
            {
                RPGObject obj = fight.Force2[index];
                if (obj != null && obj.PlayerId == playerId && obj.ObjectId == objectId)
                {
                    return obj;
                }
            }

            return null;
        }

        /// <summary>
        /// 在本场战斗中寻找一个敌方的随机单位,敌人的生命值要大于0
        /// </summary>
        /// <param name="fight"></param>
        /// <param name="playerId"></param>
        /// <param name="objectId"></param>
        /// <returns></returns>
        private static RPGObject GetRandomEnemyObject(RPGFight fight, uint playerId, uint objectId)
        {
            bool isInForce1 = true;
            for (int index = 0; index < fight.Force1.Length; index++)
            {
                RPGObject obj = fight.Force1[index];
                if (obj != null && obj.PlayerId == playerId && obj.ObjectId == objectId)
                {
                    // 在force1里寻找到了当前单位，那么敌人单位在force2中
                    isInForce1 = false;
                }
            }

            List<RPGObject> enemys = new List<RPGObject>();
            if (isInForce1)
            {
                for (int index = 0; index < fight.Force1.Length; index++)
                {
                    if (fight.Force1[index] != null && fight.Force1[index].HP > 0)
                    {
                        enemys.Add(fight.Force1[index]);
                    }
                }
            }
            else
            {
                for (int index = 0; index < fight.Force2.Length; index++)
                {
                    if (fight.Force2[index] != null && fight.Force2[index].HP > 0)
                    {
                        enemys.Add(fight.Force2[index]);
                    }
                }
            }
            int randTargetIndex = new Random().Next(0, enemys.Count);
            return enemys[randTargetIndex];
        }
    }
}
